package com.icehelper.hades.base.util;

import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;

import java.security.InvalidParameterException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * 校验工具类
 */
public class ValidateUtil{

  private ValidateUtil() {
  }

  private static final int[] POWER_LIST = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};

  private static final int[] PARITY_BIT = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};

  private static final String YEAR_PREFIX = "19";

  private static final Map<Integer, String> ZONE_NUM = new HashMap<>(100);

  private static final Pattern VIN_PATTERN = Pattern.compile("[A-HJ-NPR-Z0-9]{17}");

  private static final Pattern PLATE_NO_PATTERN = Pattern
      .compile("^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z][A-Z][A-Z0-9]{4,5}[A-Z0-9挂学警港澳]$");

  /**
   * 人名
   */
  private static final Pattern OWNER_NAME = Pattern.compile("^[\\u4e00-\\u9fa5]{2,5}$");

  /**
   * 企业名称,至少3个汉字
   */
  private static final Pattern COMPANY_NAME = Pattern.compile("^[\\u4e00-\\u9fa5]{3,}$");

  /**
   * 企业电话
   */
  private static final Pattern COMPANY_PHONE = Pattern.compile("^0[0-9]{2,3}[1-9][0-9]{6,7}$");

  /**
   * 银行卡
   */
  private static final Pattern BANK_CARD = Pattern.compile("^[0-9]{13,19}$");

  /**
   * 手机号
   */
  private static final Pattern MOBILE = Pattern.compile("^1[0-9]{10}$");

  private static final Map<Integer, Integer> VIN_MAP_WEIGHTING = new HashMap<>(20);

  private static final Map<Character, Integer> VIN_MAP_VALUE = new HashMap<>(20);

  private static final int[] POWER = {1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28};

  /**
   * 社会统一信用代码不含（I、O、S、V、Z） 等字母
   */
  private static final char[] SOCIAL_CREDIT_CODE =
      {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L',
          'M', 'N', 'P', 'Q', 'R', 'T', 'U', 'W', 'X', 'Y'};

  static {
    ZONE_NUM.put(11, "北京");
    ZONE_NUM.put(12, "天津");
    ZONE_NUM.put(13, "河北");
    ZONE_NUM.put(14, "山西");
    ZONE_NUM.put(15, "内蒙古");
    ZONE_NUM.put(21, "辽宁");
    ZONE_NUM.put(22, "吉林");
    ZONE_NUM.put(23, "黑龙江");
    ZONE_NUM.put(31, "上海");
    ZONE_NUM.put(32, "江苏");
    ZONE_NUM.put(33, "浙江");
    ZONE_NUM.put(34, "安徽");
    ZONE_NUM.put(35, "福建");
    ZONE_NUM.put(36, "江西");
    ZONE_NUM.put(37, "山东");
    ZONE_NUM.put(41, "河南");
    ZONE_NUM.put(42, "湖北");
    ZONE_NUM.put(43, "湖南");
    ZONE_NUM.put(44, "广东");
    ZONE_NUM.put(45, "广西");
    ZONE_NUM.put(46, "海南");
    ZONE_NUM.put(50, "重庆");
    ZONE_NUM.put(51, "四川");
    ZONE_NUM.put(52, "贵州");
    ZONE_NUM.put(53, "云南");
    ZONE_NUM.put(54, "西藏");
    ZONE_NUM.put(61, "陕西");
    ZONE_NUM.put(62, "甘肃");
    ZONE_NUM.put(63, "青海");
    ZONE_NUM.put(64, "宁夏");
    ZONE_NUM.put(65, "新疆");
    ZONE_NUM.put(71, "台湾");
    ZONE_NUM.put(81, "香港");
    ZONE_NUM.put(82, "澳门");
    ZONE_NUM.put(91, "外国");

    VIN_MAP_WEIGHTING.put(1, 8);
    VIN_MAP_WEIGHTING.put(2, 7);
    VIN_MAP_WEIGHTING.put(3, 6);
    VIN_MAP_WEIGHTING.put(4, 5);
    VIN_MAP_WEIGHTING.put(5, 4);
    VIN_MAP_WEIGHTING.put(6, 3);
    VIN_MAP_WEIGHTING.put(7, 2);
    VIN_MAP_WEIGHTING.put(8, 10);
    VIN_MAP_WEIGHTING.put(9, 0);
    VIN_MAP_WEIGHTING.put(10, 9);
    VIN_MAP_WEIGHTING.put(11, 8);
    VIN_MAP_WEIGHTING.put(12, 7);
    VIN_MAP_WEIGHTING.put(13, 6);
    VIN_MAP_WEIGHTING.put(14, 5);
    VIN_MAP_WEIGHTING.put(15, 4);
    VIN_MAP_WEIGHTING.put(16, 3);
    VIN_MAP_WEIGHTING.put(17, 2);

    VIN_MAP_VALUE.put('0', 0);
    VIN_MAP_VALUE.put('1', 1);
    VIN_MAP_VALUE.put('2', 2);
    VIN_MAP_VALUE.put('3', 3);
    VIN_MAP_VALUE.put('4', 4);
    VIN_MAP_VALUE.put('5', 5);
    VIN_MAP_VALUE.put('6', 6);
    VIN_MAP_VALUE.put('7', 7);
    VIN_MAP_VALUE.put('8', 8);
    VIN_MAP_VALUE.put('9', 9);
    VIN_MAP_VALUE.put('A', 1);
    VIN_MAP_VALUE.put('B', 2);
    VIN_MAP_VALUE.put('C', 3);
    VIN_MAP_VALUE.put('D', 4);
    VIN_MAP_VALUE.put('E', 5);
    VIN_MAP_VALUE.put('F', 6);
    VIN_MAP_VALUE.put('G', 7);
    VIN_MAP_VALUE.put('H', 8);
    VIN_MAP_VALUE.put('J', 1);
    VIN_MAP_VALUE.put('K', 2);
    VIN_MAP_VALUE.put('M', 4);
    VIN_MAP_VALUE.put('L', 3);
    VIN_MAP_VALUE.put('N', 5);
    VIN_MAP_VALUE.put('P', 7);
    VIN_MAP_VALUE.put('R', 9);
    VIN_MAP_VALUE.put('S', 2);
    VIN_MAP_VALUE.put('T', 3);
    VIN_MAP_VALUE.put('U', 4);
    VIN_MAP_VALUE.put('V', 5);
    VIN_MAP_VALUE.put('W', 6);
    VIN_MAP_VALUE.put('X', 7);
    VIN_MAP_VALUE.put('Y', 8);
    VIN_MAP_VALUE.put('Z', 9);
  }

  public static BindingResult validate(Object o) {
    String[] beanNames = BeanUtil.getBeanFactory().getBeanNamesForType( Validator.class);
    if (beanNames == null || beanNames.length <= 0) {
      throw new IllegalArgumentException("无法找到Validator");
    }
    Validator validator = (Validator) BeanUtil.getBeanFactory().getBean(beanNames[beanNames.length - 1]);
    if (validator == null) {
      throw new IllegalArgumentException("无法找到Validator");
    }
    BindingResult bindingResult = new BeanPropertyBindingResult(o, o.getClass().getSimpleName());
    validator.validate(o, bindingResult);
    return bindingResult;
  }

  /**
   * 校验车牌号是否合法
   *
   * @param plateNo 车牌号
   * @return 是否合法
   */
  public static boolean isValidPlateNo(String plateNo) {
    return StringUtils.isNotBlank(plateNo) && PLATE_NO_PATTERN.matcher(plateNo).matches();
  }

  /**
   * 身份证验证
   *
   * @param certNo 身份证号码
   * @return 是否有效
   */
  public static boolean isValidCertificateNo(String certNo) {
    boolean isInvalid = certNo == null || (certNo.length() != 15 && certNo.length() != 18);
    if (isInvalid) {
      return false;
    }
    final char[] cs = certNo.toUpperCase().toCharArray();
    // 校验位数
    int power = checksum(cs);
    // 校验"校验码"
    if (certNo.length() == 18 && cs[cs.length - 1] != PARITY_BIT[power % 11]) {
      return false;
    }
    // 校验区位码
    if (!ZONE_NUM.containsKey(Integer.valueOf(certNo.substring(0, 2)))) {
      return false;
    }

    // 校验年份
    String year = certNo.length() == 15 ? YEAR_PREFIX + certNo.substring(6, 8) : certNo.substring(6, 10);

    final int iYear = Integer.parseInt(year);
    // 1900年的PASS，超过今年的PASS
    if (iYear < 1900 || iYear > Calendar.getInstance().get(Calendar.YEAR)) {
      return false;
    }

    // 校验月份
    String month = certNo.length() == 15 ? certNo.substring(8, 10) : certNo.substring(10, 12);
    final int iMonth = Integer.parseInt(month);
    if (iMonth < 1 || iMonth > 12) {
      return false;
    }

    // 校验天数
    String day = certNo.length() == 15 ? certNo.substring(10, 12) : certNo.substring(12, 14);
    final int iDay = Integer.parseInt(day);
    if (iDay < 1 || iDay > 31) {
      return false;
    }
    return true;

  }

  /**
   * 校验身份证位数是否合法,并且返回权重
   *
   * @param cs 身份证字符数组
   * @return 位数是否合法
   */
  private static int checksum(char[] cs) {
    int power = 0;
    for (int i = 0; i < cs.length - 1; i++) {
      if (i < cs.length - 1) {
        power += (cs[i] - '0') * POWER_LIST[i];
      }
    }
    return power;
  }

  /**
   * 确认是不是正确的发动机号
   *
   * @param engineNo 发动机号
   * @return 验证结果
   */
  public static boolean isValidEngineNo(String engineNo) {
    return StringUtils.isNotBlank(engineNo) && engineNo.length() > 1 && engineNo.length() < 50 && !engineNo
        .contains("*");

  }

  /**
   * 判断vin码是否合法,通过校验vin码的格式和校验位是否正确
   *
   * @param vin vin
   * @return 是否合法
   */
  public static boolean isValidVin(String vin) {
    if (StringUtils.isBlank(vin)) {
      return false;
    }
    if (!VIN_PATTERN.matcher(vin).matches()) {
      return false;
    }
    char[] vinArr = vin.toCharArray();
    int amount = 0;
    for (int i = 0; i < vinArr.length; i++) {
      //VIN码从从第一位开始，码数字的对应值×该位的加权值，计算全部17位的乘积值相加
      amount += VIN_MAP_VALUE.get(vinArr[i]) * VIN_MAP_WEIGHTING.get(i + 1);
    }
    //计算全部17位的乘积值相加除以11，所得的余数，即为第九位校验值

    int result = amount % 11;
    //乘积值相加除以11、若余数为10，即为字母Ｘ
    if (result == 10) {
      return vinArr[8] == 'X';
    } else {
      return result == VIN_MAP_VALUE.get(vinArr[8]);
    }
  }

  /**
   * 车辆注册日期是否合法
   *
   * @param registerDate 注册日期
   * @return 车辆注册日期是否合法
   */
  public static boolean isValidRegisterDate(Date registerDate) {
    return registerDate != null && registerDate.getTime() < System.currentTimeMillis();
  }

  /**
   * 判断某个参数是否为空
   *
   * @param object 参数
   * @param description 参数描述
   */
  public static void notNull(Object object, String description) {
    if (object == null) {
      description = StringUtils.isBlank(description) ? "参数" : description;
      throw new InvalidParameterException(description + "为空");
    }
  }

  /**
   * 判断某个字符串是否为空
   *
   * @param text 参数
   * @param description 参数描述
   */
  public static void notBlank(CharSequence text, String description) {
    if (StringUtils.isBlank(text)) {
      description = StringUtils.isBlank(description) ? "参数" : description;
      throw new InvalidParameterException(description + "为空");
    }
  }

  /**
   * 校验企业社会信用代码是否正确
   *
   * @param creditCode 企业社会信用代码
   * @return 企业社会信用代码是否正确
   */
  public static boolean isValidCompanyCreditCode(String creditCode) {
    if (StringUtils.length(creditCode) != 18) {
      return false;
    }
    Map<String, Integer> datas = new HashMap<>(20);
    for (int i = 0; i < SOCIAL_CREDIT_CODE.length; i++) {
      datas.put(SOCIAL_CREDIT_CODE[i] + "", i);
    }
    char[] chars = creditCode.substring(0, 17).toCharArray();
    int sum = 0;
    for (int i = 0; i < chars.length; i++) {
      Integer code = datas.get(chars[i] + "");
      if (code == null) {
        return false;
      }
      sum += POWER[i] * code;
    }

    int temp = sum % 31;
    temp = temp == 0 ? 31 : temp;
    return creditCode.substring(17, 18).equals(SOCIAL_CREDIT_CODE[31 - temp] + "");
  }


  /**
   * 车险投保保人姓名校验规则-非全角-2-5个汉字
   *
   * @param name 名字
   * @return 是否正确
   */
  public static boolean isValidVehicleOwnerName(String name) {
    return StringUtils.isNotBlank(name) && OWNER_NAME.matcher(name).matches();
  }

  /**
   * 校验企业名称是否正确
   *
   * @param companyName 企业名称
   * @return 是否正确
   */
  public static boolean isValidCompanyName(String companyName) {
    return StringUtils.isNotBlank(companyName) && COMPANY_NAME.matcher(companyName).matches();
  }

  /**
   * 校验银行卡号是否正确
   *
   * @param bankCard 银行卡号
   * @return 是否正确
   */
  public static boolean isValidBankCard(String bankCard) {
    return StringUtils.isNotBlank(bankCard) && BANK_CARD.matcher(bankCard).matches();
  }

  /**
   * 校验企业电话号是否正确
   *
   * @param companyPhone 单位电话号码
   * @return 是否正确
   */
  public static boolean isValidCompanyPhone(String companyPhone) {
    return StringUtils.isNotBlank(companyPhone) && COMPANY_PHONE.matcher(companyPhone).matches();
  }


  /**
   * 校验手机号是否正确
   *
   * @param mobile 手机号号码
   * @return 是否正确
   */
  public static boolean isValidMobile(String mobile) {
    return StringUtils.isNotBlank(mobile) && MOBILE.matcher(mobile).matches();
  }
}
